package com.quiz.bis.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.quiz.bis.domain.bo.BisPaperAutoBo;
import com.quiz.bis.domain.model.*;
import com.quiz.bis.domain.vo.BisPaperJsonVo;
import com.quiz.bis.domain.vo.BisPaperQuestionVo;
import com.quiz.bis.domain.vo.BisPapersDetailVo;
import com.quiz.bis.mapper.*;
import com.quiz.bis.service.IBisPaperAutoService;
import com.quiz.common.core.domain.R;
import com.quiz.common.exception.ServiceException;
import com.quiz.common.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 试卷自动生成服务实现类
 *
 * @author ahuan
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class BisPaperAutoServiceImpl implements IBisPaperAutoService {
    private final BisQuestionsMapper questionsMapper;
    private final BisOptionsMapper optionsMapper;
    private final BisKnowPointsMapper knowPointsMapper;
    private final BisPaperMapper paperMapper;
    private final BisPaperQuestionMapper paperQuestionMapper;
    private final BisCategoriesMapper categoriesMapper;

    /**
     * 自动组卷
     */
    @Transactional
    @Override
    public R<BisPapersDetailVo> autoGeneratePaper(BisPaperAutoBo request) {
        // 1. 验证参数
        validateRequest(request);

        // 2. 获取分类下的所有知识点
        List<BisKnowPoints> knowPoints = knowPointsMapper.selectList(
                Wrappers.<BisKnowPoints>lambdaQuery()
                        .eq(BisKnowPoints::getCategoryId, request.getCategoryId())
        );

        if (knowPoints.isEmpty()) {
            throw new ServiceException("该分类下没有知识点，无法组卷");
        }

        // 3. 获取所有题目并按类型和难度分类
        Map<String, Map<String, List<BisQuestions>>> categorizedQuestions = getCategorizedQuestions(request.getCategoryId());

        // 4. 按照配置分配题目（传入scoreConfig）
        PaperGenerationResult generationResult = distributeQuestions(
                request.getQuestionCountConfig(),
                request.getDifficultyConfig(),
                request.getQuestionScoreConfig(), // 传入题型分数配置
                knowPoints,
                categorizedQuestions
        );

        // 5. 保存试卷
        BisPapers paper = savePaper(request, generationResult.getTotalScore());

        // 6. 保存试卷题目关联
        savePaperQuestions(paper.getPaperId(), generationResult.getSelectedQuestions());

        // 7. 构建返回结果
        BisPapersDetailVo result = buildPaperDetailVo(paper, generationResult.getSelectedQuestions(), request.getQuestionScoreConfig());

        // 验证数据
        log.info("试卷生成完成 - 试卷ID: {}, 总题数: {}",
                result.getPaperId(),
                generationResult.getSelectedQuestions().size());
        log.info("JSON数据详情: {}", result.getJsonData());

        return R.ok(result);
    }

    private void validateRequest(BisPaperAutoBo request) {
        if (request.getCategoryId() == null) {
            throw new ServiceException("分类ID不能为空");
        }

        // 验证题目数量配置
        if (request.getQuestionCountConfig() == null) {
            throw new ServiceException("题目数量配置不能为空");
        }

        // 验证难度配置
        if (request.getDifficultyConfig() != null) {
            double totalRatio = request.getDifficultyConfig().getEasyRatio() +
                    request.getDifficultyConfig().getSlightlyEasyRatio() +
                    request.getDifficultyConfig().getMediumRatio() +
                    request.getDifficultyConfig().getSlightlyHardRatio() +
                    request.getDifficultyConfig().getHardRatio();

            if (Math.abs(totalRatio - 1.0) > 0.0001) {
                throw new ServiceException("难度比例总和必须等于1");
            }
        }
    }

    private Map<String, Map<String, List<BisQuestions>>> getCategorizedQuestions(Long categoryId) {
        // 查询该分类下的所有题目
        List<BisQuestions> questions = questionsMapper.selectList(
                Wrappers.<BisQuestions>lambdaQuery()
                        .eq(BisQuestions::getCategoryId, categoryId)
        );

        // 按题型和难度分类
        Map<String, Map<String, List<BisQuestions>>> categorizedQuestions = new HashMap<>();

        for (BisQuestions question : questions) {
            categorizedQuestions
                    .computeIfAbsent(question.getQuestionType(), k -> new HashMap<>())
                    .computeIfAbsent(question.getDifficulty(), k -> new ArrayList<>())
                    .add(question);
        }

        return categorizedQuestions;
    }

    private PaperGenerationResult distributeQuestions(
            BisPaperAutoBo.QuestionCountConfig countConfig,
            BisPaperAutoBo.DifficultyConfig difficultyConfig,
            BisPaperAutoBo.QuestionScoreConfig scoreConfig, // 新增参数
            List<BisKnowPoints> knowPoints,
            Map<String, Map<String, List<BisQuestions>>> categorizedQuestions) {

        List<BisPaperQuestionVo> selectedQuestions = new ArrayList<>();
        int totalScore = 0;

        // 分配单选题
        if (countConfig.getSingleChoiceCount() > 0) {
            List<BisQuestions> singleQuestions = selectQuestions(
                    "single",
                    countConfig.getSingleChoiceCount(),
                    difficultyConfig,
                    knowPoints,
                    categorizedQuestions
            );
            selectedQuestions.addAll(convertToPaperQuestionVo(singleQuestions, scoreConfig.getSingleChoiceScore()));
            totalScore += singleQuestions.size() * scoreConfig.getSingleChoiceScore();
        }

        // 分配多选题
        if (countConfig.getMultipleChoiceCount() > 0) {
            List<BisQuestions> multipleQuestions = selectQuestions(
                    "multiple",
                    countConfig.getMultipleChoiceCount(),
                    difficultyConfig,
                    knowPoints,
                    categorizedQuestions
            );
            selectedQuestions.addAll(convertToPaperQuestionVo(multipleQuestions, scoreConfig.getMultipleChoiceScore()));
            totalScore += multipleQuestions.size() * scoreConfig.getMultipleChoiceScore();
        }

        // 分配填空题
        if (countConfig.getFillBlankCount() > 0) {
            List<BisQuestions> fillQuestions = selectQuestions(
                    "fill",
                    countConfig.getFillBlankCount(),
                    difficultyConfig,
                    knowPoints,
                    categorizedQuestions
            );
            selectedQuestions.addAll(convertToPaperQuestionVo(fillQuestions, scoreConfig.getFillBlankScore()));
            totalScore += fillQuestions.size() * scoreConfig.getFillBlankScore();
        }

        // 分配判断题
        if (countConfig.getTrueFalseCount() > 0) {
            List<BisQuestions> judgeQuestions = selectQuestions(
                    "judge",
                    countConfig.getTrueFalseCount(),
                    difficultyConfig,
                    knowPoints,
                    categorizedQuestions
            );
            selectedQuestions.addAll(convertToPaperQuestionVo(judgeQuestions, scoreConfig.getTrueFalseScore()));
            totalScore += judgeQuestions.size() * scoreConfig.getTrueFalseScore();
        }

        // 分配问答题
        if (countConfig.getEssayCount() > 0) {
            List<BisQuestions> essayQuestions = selectQuestions(
                    "essay",
                    countConfig.getEssayCount(),
                    difficultyConfig,
                    knowPoints,
                    categorizedQuestions
            );
            selectedQuestions.addAll(convertToPaperQuestionVo(essayQuestions, scoreConfig.getEssayScore()));
            totalScore += essayQuestions.size() * scoreConfig.getEssayScore();
        }

        return new PaperGenerationResult(selectedQuestions, totalScore);
    }

    private List<BisQuestions> selectQuestions(
            String questionType,
            int count,
            BisPaperAutoBo.DifficultyConfig difficultyConfig,
            List<BisKnowPoints> knowPoints,
            Map<String, Map<String, List<BisQuestions>>> categorizedQuestions) {

        List<BisQuestions> selectedQuestions = new ArrayList<>();

        // 如果没有这种类型的题目，直接返回空列表
        if (!categorizedQuestions.containsKey(questionType)) {
            return selectedQuestions;
        }

        // 获取该类型下所有题目按难度分类
        Map<String, List<BisQuestions>> questionsByDifficulty = categorizedQuestions.get(questionType);

        // 如果没有难度配置，随机选择题目
        if (difficultyConfig == null) {
            List<BisQuestions> allQuestions = questionsByDifficulty.values().stream()
                    .flatMap(List::stream)
                    .collect(Collectors.toList());

            Collections.shuffle(allQuestions);
            return allQuestions.stream().limit(count).collect(Collectors.toList());
        }

        // 按照难度比例分配题目数量
        int easyCount = (int) Math.round(count * difficultyConfig.getEasyRatio());
        int slightlyEasyCount = (int) Math.round(count * difficultyConfig.getSlightlyEasyRatio());
        int mediumCount = (int) Math.round(count * difficultyConfig.getMediumRatio());
        int slightlyHardCount = (int) Math.round(count * difficultyConfig.getSlightlyHardRatio());
        int hardCount = count - easyCount - slightlyEasyCount - mediumCount - slightlyHardCount;

        // 确保每个难度都有题目，尽量覆盖所有知识点
        selectedQuestions.addAll(selectQuestionsByDifficulty(questionsByDifficulty, "1", easyCount, knowPoints));
        selectedQuestions.addAll(selectQuestionsByDifficulty(questionsByDifficulty, "2", slightlyEasyCount, knowPoints));
        selectedQuestions.addAll(selectQuestionsByDifficulty(questionsByDifficulty, "3", mediumCount, knowPoints));
        selectedQuestions.addAll(selectQuestionsByDifficulty(questionsByDifficulty, "4", slightlyHardCount, knowPoints));
        selectedQuestions.addAll(selectQuestionsByDifficulty(questionsByDifficulty, "5", hardCount, knowPoints));

        // 如果题目数量不足，从其他难度补充
        if (selectedQuestions.size() < count) {
            List<BisQuestions> remainingQuestions = new ArrayList<>();
            for (List<BisQuestions> questions : questionsByDifficulty.values()) {
                remainingQuestions.addAll(questions.stream()
                        .filter(q -> !selectedQuestions.contains(q))
                        .collect(Collectors.toList()));
            }

            Collections.shuffle(remainingQuestions);
            int needed = count - selectedQuestions.size();
            selectedQuestions.addAll(remainingQuestions.stream().limit(needed).collect(Collectors.toList()));
        }

        return selectedQuestions;
    }

    private List<BisQuestions> selectQuestionsByDifficulty(
            Map<String, List<BisQuestions>> questionsByDifficulty,
            String difficulty,
            int count,
            List<BisKnowPoints> knowPoints) {

        List<BisQuestions> selected = new ArrayList<>();

        if (count <= 0 || !questionsByDifficulty.containsKey(difficulty)) {
            return selected;
        }

        List<BisQuestions> questions = new ArrayList<>(questionsByDifficulty.get(difficulty));
        Collections.shuffle(questions);

        // 优先选择覆盖不同知识点的题目
        Set<Long> coveredPoints = new HashSet<>();
        for (BisQuestions question : questions) {
            if (selected.size() >= count) {
                break;
            }

            if (question.getPointId() != null && !coveredPoints.contains(question.getPointId())) {
                selected.add(question);
                coveredPoints.add(question.getPointId());
            }
        }

        // 如果还有剩余数量，补充其他题目
        if (selected.size() < count) {
            for (BisQuestions question : questions) {
                if (selected.size() >= count) {
                    break;
                }

                if (!selected.contains(question)) {
                    selected.add(question);
                }
            }
        }

        return selected;
    }

    private List<BisPaperQuestionVo> convertToPaperQuestionVo(List<BisQuestions> questions, int score) {
        return questions.stream()
                .map(q -> {
                    BisPaperQuestionVo vo = new BisPaperQuestionVo();
                    vo.setQuestionId(q.getQuestionId());
                    vo.setQuestionTitle(q.getQuestionTitle());
                    vo.setQuestionText(q.getQuestionText());
                    vo.setQuestionTextPlain(q.getQuestionTextPlain());
                    vo.setQuestionType(q.getQuestionType());
                    vo.setDifficulty(q.getDifficulty());
                    vo.setCategoryId(q.getCategoryId());
                    vo.setPointId(q.getPointId());
                    vo.setCorrectAnswer(q.getCorrectAnswer());
                    vo.setCorrectAnswerPlain(q.getCorrectAnswerPlain());
                    vo.setQuestionScore(score);

                    // 查询并设置选项
                    List<BisOptions> options = optionsMapper.selectList(
                            Wrappers.<BisOptions>lambdaQuery()
                                    .eq(BisOptions::getQuestionId, q.getQuestionId())
                    );
                    vo.setOptions(options);

                    return vo;
                })
                .collect(Collectors.toList());
    }

    private BisPapers savePaper(BisPaperAutoBo request, int totalScore) {
        BisPapers paper;

        if (request.getPaperId() != null) {
            // 更新已有试卷
            paper = paperMapper.selectById(request.getPaperId());
            if (paper == null) {
                throw new ServiceException("试卷不存在");
            }

            paper.setPaperName(request.getPaperName());
            paper.setDescription(request.getDescription());
            paper.setTotalScore(totalScore);
            paper.setPassScore(request.getPassScore());
            paperMapper.updateById(paper);

            // 删除原有的题目关联
            paperQuestionMapper.delete(
                    Wrappers.<BisPaperQuestions>lambdaQuery()
                            .eq(BisPaperQuestions::getPaperId, request.getPaperId())
            );
        } else {
            // 创建新试卷
            paper = new BisPapers();
            paper.setPaperName(request.getPaperName());
            paper.setDescription(request.getDescription());
            paper.setTotalScore(totalScore);
            paper.setPassScore(request.getPassScore());
            paper.setStatus("draft");
            paper.setCreateBy(SecurityUtils.getUserId().toString());
            paperMapper.insert(paper);
        }

        return paper;
    }

    private void savePaperQuestions(Long paperId, List<BisPaperQuestionVo> questions) {
        int order = 1;

        for (BisPaperQuestionVo question : questions) {
            BisPaperQuestions pq = new BisPaperQuestions();
            pq.setPaperId(paperId);
            pq.setQuestionId(question.getQuestionId());
            pq.setQuestionOrder(order++);
            pq.setQuestionScore(question.getQuestionScore());
            pq.setCreateBy(SecurityUtils.getUserId().toString());
            paperQuestionMapper.insert(pq);
        }
    }

    private BisPapersDetailVo buildPaperDetailVo(BisPapers paper,
                                                 List<BisPaperQuestionVo> questions,
                                                 BisPaperAutoBo.QuestionScoreConfig scoreConfig) {
        BisPapersDetailVo vo = new BisPapersDetailVo();
        vo.setPaperId(paper.getPaperId());
        vo.setPaperName(paper.getPaperName());
        vo.setDescription(paper.getDescription());
        vo.setTotalScore(paper.getTotalScore());
        vo.setPassScore(paper.getPassScore());
        vo.setStatus(paper.getStatus());

        BisPaperJsonVo jsonVo = new BisPaperJsonVo();
        jsonVo.setSingleScore(scoreConfig.getSingleChoiceScore());
        jsonVo.setMultipleScore(scoreConfig.getMultipleChoiceScore());
        jsonVo.setFillScore(scoreConfig.getFillBlankScore());
        jsonVo.setJudgeScore(scoreConfig.getTrueFalseScore());
        jsonVo.setEssayScore(scoreConfig.getEssayScore());

        jsonVo.addQuestions(questions);

        vo.setJsonData(jsonVo);
        return vo;
    }

    /**
     * 内部类，用于返回组卷结果
     */
    private static class PaperGenerationResult {
        private final List<BisPaperQuestionVo> selectedQuestions;
        private final Integer totalScore;

        public PaperGenerationResult(List<BisPaperQuestionVo> selectedQuestions, Integer totalScore) {
            this.selectedQuestions = selectedQuestions;
            this.totalScore = totalScore;
        }

        public List<BisPaperQuestionVo> getSelectedQuestions() {
            return selectedQuestions;
        }

        public Integer getTotalScore() {
            return totalScore;
        }
    }
}
